#pragma once
//文件版本
#include "../comm/util.hpp"
#include "../comm/log.hpp"

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include <cstdlib>
#include <cassert>

namespace ns_model{//模型
    using namespace std;
    using namespace ns_log;
    using namespace ns_util;

    struct Question{
        std::string number; //题目编号，唯一
        std::string title;  //题目的标题
        std::string star;   //难度: 简单 中等 困难
        int cpu_limit;      //题目的时间要求(S)
        int mem_limit;      //题目的空间要求(KB)
        std::string desc;   //题目的描述
        std::string header; //题目预设给用户在线编辑器的代码
        std::string tail;   //题目的测试用例，需要和header拼接，形成完整代码
    };

    const std::string questins_list = "./questions/questions.list";//问题目录
    const std::string questins_path = "./questions/";//问题路径

    class model{
        private:
        //题号 : 题目细节
        unordered_map<string, Question> questions;//定义了一个名为 questions 的哈希表，用来存储字符串到 Question 对象的映射。
        //字符串到Question对象的映射意味着你可以使用一个字符串（比如问题的ID或名称）来快速查找或访问与之关联的Question对象。
        public:
        Model()
        {
            assert(LoadQuestionList(questins_list));//判断这个函数是否运行成功
        }
        bool LoadQuestionList(const string &question_list){
            //加载配置文件: questions/questions.list + 题目编号文件
            ifstream in(question_list);//定义了一个输入文件流 in，用于从名为 question_list 的文件中读取数据。
            //读取到in中
            if(!in.is_open())
            {
                LOG(FATAL) << " 加载题库失败,请检查是否存在题库文件" << "\n";
                return false;
            }

            string line;
            while(getline(in, line)){//从输入文件流 in 中读取一行文本，并将其存储在字符串 line 中。
                vector<string> tokens;//定义了一个名为 tokens 的向量（动态数组），用于存储字符串类型的元素
                StringUtil::SplitString(line, &tokens, " ");//分割字符串
                // 1 判断回文数 简单 1 30000
                if(tokens.size() != 5)//检查 tokens 向量中元素的数量是否不等于5。如果数量不等于5，表达式的结果为真（true）
                {
                    LOG(WARNING) << "加载部分题目失败, 请检查文件格式" << "\n";
                    continue;
                }

                Question q;
                q.number = tokens[0];
                q.title = tokens[1];//将tokens的第一个元素赋值给q的number属性，其他类似
                q.star = tokens[2];
                q.cpu_limit = atoi(tokens[3].c_str());
                q.mem_limit = atoi(tokens[4].c_str());
                //c_str() 方法将字符串对象转换为 C 风格的字符串（const char*），而 atoi 函数则将这个 C 风格的字符串转换为整数
                string path = questins_path;
                path += q.number;
                path += "/";//路径分隔符
            
                FileUtil::ReadFile(path+"desc.txt", &(q.desc), true);//将path+"desc.txt的数据读取到&(q.desc)中
                FileUtil::ReadFile(path+"header.cpp", &(q.header), true);
                FileUtil::ReadFile(path+"tail.cpp", &(q.tail), true);

                questions.insert({q.number, q});//将 q 对象以 q.number 作为键（key）插入到 questions 容器中
            }
            LOG(INFO) << "加载题库...成功!" << "\n";
            in.close();

            return true;
        }

        bool GetAllQuestions(vector<Question> *out){
            if(questions.size() == 0)//检查 questions 这个容器是否为空
            {
                LOG(ERROR) << "用户获取题库失败" << "\n";
                return false;
            }
            for(const auto &q : questions){
                out->push_back(q.second); //first: key, second: value
                //遍历 questions 容器中的每个元素，并将每个元素的 value（即 second）添加到 out 容器中
            }

            return true;
        }

        bool GetOneQuestion(const std::string &number, Question *q){
            const auto& iter = questions.find(number);//尝试在 questions 容器，中查找键为 number 的元素，赋值给iter
            if(iter == questions.end()){//意味着在 questions 容器中不存在键为 number 的元素。
                //questions.end() 是一个迭代器，指向容器末尾的“假想”元素，用于表示查找失败
                LOG(ERROR) << "用户获取题目失败, 题目编号: " << number << "\n";
                return false;
            }
            (*q) = iter->second;//将找到的元素的 value（即 iter->second）赋值给 *q

            return true;
        }

        ~Model()
        {}
    };
}